/*
 * tcp_ip_client.c
 *
 *  Created on: Aug 20, 2012
 *      Author: drew
 */

#include "tcp_ip_server.h"

#define GETBUFFSIZE( x )  sizeof( x )/sizeof( char )

void * vpTcpIpServerRunLoop( void * pvParameters )
{
  xpTcpIpServerDescription xpServerObj = (xpTcpIpServerDescription)pvParameters;

  char cReadBuffer[1024];
  int iSinSize = sizeof( struct sockaddr_in );

  /* Once entered the server loop never exits */
  /* TODO: Allow the server to exit, and start on demand */
  for( ;; )
  {
    xpServerObj->iConnection = accept( xpServerObj->iSocket, (struct sockaddr *)&xpServerObj->xClientAddress, (socklen_t *)&iSinSize );

    if( -1 == xpServerObj->iConnection )
    {
      perror( "Server Connection" );
    }

    pthread_mutex_lock( &xpServerObj->xMutex );
    xpServerObj->iIsConnected = 1;
    pthread_mutex_unlock( &xpServerObj->xMutex );

    for( ;; )
    {
      if( read( xpServerObj->iConnection, cReadBuffer, 1 ) <= 0 )
      {
        /* Socket closed by client */
        break;
      }

      xpServerObj->vpCallback( cReadBuffer, 1 );
    }

  }

  return NULL;
}

xpTcpIpServerDescription xpTcpIpServerCreateObject( int iPort, vpTcpIpServerRxCallback vpCallback )
{
  xpTcpIpServerDescription xpServerObj = (xpTcpIpServerDescription)malloc( sizeof( xTcpIpServerDescription ) );

  xpServerObj->iPort = iPort;
  xpServerObj->vpCallback = vpCallback;
  xpServerObj->iIsConnected = 0;

  pthread_mutex_init( &xpServerObj->xMutex, NULL );

  return xpServerObj;
}

void vTcpIpServerDestroyObject( xpTcpIpServerDescription xpServerObj )
{
  free( xpServerObj );
}

int iTcpIpServerStart( xpTcpIpServerDescription xpServerObj )
{
  int iOption = 1;
  if( ( xpServerObj->iSocket = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )

  {
      perror("Socket");
  }

  if( setsockopt( xpServerObj->iSocket, SOL_SOCKET, SO_REUSEADDR, &iOption, sizeof( int ) ) == -1 )
  {
    perror("Setsockopt");
    return 1;
  }

  xpServerObj->xServerAddress.sin_family = AF_INET;
  xpServerObj->xServerAddress.sin_port = htons( xpServerObj->iPort );
  xpServerObj->xServerAddress.sin_addr.s_addr = INADDR_ANY;

  memset( &xpServerObj->xServerAddress.sin_zero, '\0', 8 );

  if( bind( xpServerObj->iSocket, (struct sockaddr *)&xpServerObj->xServerAddress, sizeof( struct sockaddr ) ) == -1)
  {
      perror("Unable to bind");
      return 1;
  }

  if( listen( xpServerObj->iSocket, 1 ) == -1)
  {
      perror("Listen");
      return 1;
  }

  if( pthread_create( &xpServerObj->xThreadHandle, NULL, vpTcpIpServerRunLoop, (void*)xpServerObj ) )
  {
    return 1;
  }

  return 0;
}

void vTcpIpServerStop( xpTcpIpServerDescription xpServerObj )
{
  pthread_mutex_destroy( &xpServerObj->xMutex );
  close( xpServerObj->iConnection );
  close( xpServerObj->iSocket );
}

int iTcpIpServerPrint( xpTcpIpServerDescription xpServerObj, char * cpBuff, int iLength )
{
  int iReadReturn;
  iReadReturn = write( xpServerObj->iConnection, cpBuff, iLength );

  if( iReadReturn < 0 )
  {
    perror( "Write" );
  }

  if( iReadReturn == 0 )
  {
    printf("ERROR: Socket is closed\n");
  }

  return (iReadReturn <= 0);
}

void iTcpIpServerWaitForSocketClose( xpTcpIpServerDescription xpServerObj )
{
  pthread_join( xpServerObj->xThreadHandle, NULL );
}

int iTcpIpServerIsConnected( xpTcpIpServerDescription xpServerObj )
{
  int iReturnValue = 0;

  pthread_mutex_lock( &xpServerObj->xMutex );

  iReturnValue = xpServerObj->iIsConnected;

  printf( "Is Server Connected: %d\n", xpServerObj->iIsConnected );

  pthread_mutex_unlock( &xpServerObj->xMutex );

  return iReturnValue;
}
